Connection Limiting
Connection limiting restricts the number of simultaneous (concurrent) connections a client can open to your NGINX server.
It is implemented using the limit_conn module and protects against:
- Slowloris attacks
- Connection-flood DDoS attacks
- Resource exhaustion (worker connections, memory)
- Abuse from bots and crawlers
- Misbehaving clients holding connections open
Key difference from rate limiting:
limit_connlimits how many connections are open at the same time, not how many requests are sent per second.
How limit_conn Works (Internals)
NGINX maintains a shared memory counter:
- Each new connection increments a counter
- Counter is keyed by:
- IP address
- Server
- Custom variable
- When the limit is exceeded:
- Connection is rejected immediately
- Counter is decremented when the connection closes
This is a hard limit (no queuing, no delays).
Core Directives of limit_conn
limit_conn_zone (Define the Connection Zone)
limit_conn_zone $binary_remote_addr zone=conn_limit:10m;
| Component | Meaning |
|---|---|
$binary_remote_addr | Client identifier (IP) |
zone=conn_limit:10m | Shared memory zone |
10m | Stores ~160,000 unique keys |
Uses binary format → more memory-efficient
limit_conn (Apply the Limit)
limit_conn conn_limit 10;
| Part | Meaning |
|---|---|
conn_limit | Zone name |
10 | Max concurrent connections |
limit_conn_status (Custom Status Code)
limit_conn_status 429;
Default is 503 Service Unavailable
Basic Example: Limit Connections Per IP
http {
limit_conn_zone $binary_remote_addr zone=per_ip:10m;
server {
listen 80;
location / {
limit_conn per_ip 10;
}
}
}
- Each IP can open 10 concurrent connections
- 11th connection is rejected
Protecting Against Slowloris Attacks
Slowloris holds many connections open with slow headers.
limit_conn_zone $binary_remote_addr zone=slowloris:10m;
server {
listen 443 ssl;
limit_conn slowloris 5;
client_header_timeout 10s;
client_body_timeout 10s;
}
- Max 5 open connections per IP
- Timeouts close slow connections
Limiting Connections Per Server (Global Cap)
limit_conn_zone $server_name zone=per_server:10m;
server {
listen 80;
limit_conn per_server 1000;
}
- Backend has limited capacity
- You want a hard cap per virtual host
API / WebSocket Connection Limiting
WebSockets keep connections open for a long time.
limit_conn_zone $binary_remote_addr zone=ws:10m;
location /ws/ {
limit_conn ws 2;
proxy_pass http://ws_backend;
}
Combining limit_conn with limit_req
limit_req_zone $binary_remote_addr zone=req:10m rate=10r/s;
limit_conn_zone $binary_remote_addr zone=conn:10m;
server {
listen 443 ssl;
location /api/ {
limit_req zone=req burst=20 nodelay;
limit_conn conn 10;
proxy_pass http://api_backend;
}
}
| Threat | Protection |
|---|---|
| HTTP flood | limit_req |
| Slow clients | limit_conn |
| Resource exhaustion | Both |
Limiting Connections Behind a Load Balancer
Fix Real Client IP
set_real_ip_from 10.0.0.0/8;
real_ip_header X-Forwarded-For;
Then:
limit_conn_zone $binary_remote_addr zone=realip:10m;
limit_conn realip 20;
📌 Without this:
- All users appear as one IP
- Limits become useless
Common Security Mistakes
| Mistake | Risk |
|---|---|
| Too high connection limit | Ineffective protection |
| Too low limit | Legit users blocked |
| Not setting timeouts | Slowloris still possible |
| No real IP handling | All users share same bucket |
Only using limit_conn | HTTP floods still succeed |
Testing Connection Limits
Open Multiple Connections
ab -n 1000 -c 50 http://example.com/
Expected: Some requests fail with 429 or 503
Check logs
error_log /var/log/nginx/error.log notice;
limit_conn vs limit_req
| Feature | limit_conn | limit_req |
|---|---|---|
| Limits | Concurrent connections | Requests per second |
| Queueing | ❌ No | ✔ Yes |
| Best for | Slowloris, WebSockets | APIs, login endpoints |
Recommended Secure Defaults
limit_conn_zone $binary_remote_addr zone=conn:10m;
limit_conn_status 429;
server {
limit_conn conn 10;
client_header_timeout 10s;
client_body_timeout 10s;
}